package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.service.TestService;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author mqx
 * @date 2020-11-9 14:41:11
 */
@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    //  操作redis 有两种客户端，一种：RedisTemplate
    //  一种：Jedis jedis.set(key,value); jedis.get(key)

    //  使用redisson 做分布式锁
    @Override
    public void testLock() {
        //  获取锁
        RLock lock = redissonClient.getLock("lock");
        //  上锁
        lock.lock();
        //  业务逻辑代码
        String numValue = redisTemplate.opsForValue().get("num");
        //  判断
        if(StringUtils.isEmpty(numValue)){
            return;
        }
        //  不为空！数据类型转换
        int num = Integer.parseInt(numValue);
        // redisTemplate.expire("lock",3l, TimeUnit.SECONDS);
        redisTemplate.opsForValue().set("num",String.valueOf(++num));
        //  解锁
        lock.unlock();
    }

    @Override
    public String readLock() {
        //  参考api
        RReadWriteLock rwlock = redissonClient.getReadWriteLock("anyRWLock");

        //  获取读锁对象
        RLock rLock = rwlock.readLock();

        //  开始上锁
        rLock.lock(10,TimeUnit.SECONDS);

        //  业务操作    读取缓存中的数据
        String msg = redisTemplate.opsForValue().get("msg");

        return msg;
    }

    @Override
    public String writeLock() {
        //  参考api
        RReadWriteLock rwlock = redissonClient.getReadWriteLock("anyRWLock");

        //  获取到写锁对象
        RLock rLock = rwlock.writeLock();

        //  上锁
        rLock.lock(10,TimeUnit.SECONDS);

        //  业务逻辑
        redisTemplate.opsForValue().set("msg",UUID.randomUUID().toString());

        return "写入完成了.....";
    }

    //  使用redis 做的分布式锁！
    public void testLock1() {
        /*
        1.  获取缓存中的数据（key=num） set num 0;
        2.  如果能够从key 中获取数据，则将数据进行+1操作，并写回缓存
        3.  如果获取的数据为null ,则返回！
         */
        String uuid = UUID.randomUUID().toString();
        //  执行setnx("lock","ok")
        //  set key value px 3000 nx  =  set key value ex 3 nx
        Boolean flag = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3,TimeUnit.SECONDS);
        //  如果返回true 表示执行成功，意味着上锁了！
        if (flag){
            String numValue = redisTemplate.opsForValue().get("num");
            //  判断
            if(StringUtils.isEmpty(numValue)){
                return;
            }
            //  不为空！数据类型转换
            int num = Integer.parseInt(numValue);
            // redisTemplate.expire("lock",3l, TimeUnit.SECONDS);
            //  int i = 1/0;
            redisTemplate.opsForValue().set("num",String.valueOf(++num));
            //  正常执行完成业务之后删除锁
            //            if (uuid.equals(redisTemplate.opsForValue().get("lock"))){
            //                //  index1 执行到这的时候，锁释放了，同时index2 进来了。
            //                //  此时，cpu 又将执行权限交给了index1 ,直接删除了。此时，index1删除的index2的锁！
            //                //  删除锁
            //                redisTemplate.delete("lock");
            //            }

            //  定义lua 脚本
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            //  将lua 脚本放入方法中
            redisScript.setScriptText(script);
            //  设置一个返回值
            redisScript.setResultType(Long.class);
            //  执行lua 脚本
            //  第一个RedisScript 表示只做lua 脚本对象， 第二个参数表示key ，第三个参数 上锁的时候对应的值
            redisTemplate.execute(redisScript, Arrays.asList("lock"),uuid);

        }else {
            try {
                //  睡眠
                Thread.sleep(1000);
                //  处理业务，自旋！
                testLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
//    @Override
//    public synchronized void testLock() {
//        /*
//        1.  获取缓存中的数据（key=num） set num 0;
//        2.  如果能够从key 中获取数据，则将数据进行+1操作，并写回缓存
//        3.  如果获取的数据为null ,则返回！
//         */
//        String numValue = redisTemplate.opsForValue().get("num");
//        //  判断
//        if(StringUtils.isEmpty(numValue)){
//            return;
//        }
//        //  不为空！数据类型转换
//        int num = Integer.parseInt(numValue);
//
//        redisTemplate.opsForValue().set("num",String.valueOf(++num));
//
//    }
}
